package com.bac.rentmap.tiles;

 import java.util.Enumeration;
import java.util.HashMap;
 import java.util.Properties;
import java.util.logging.Logger;

 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;

 import org.apache.tiles.TilesApplicationContext;
 import org.apache.tiles.TilesContainer;
 import org.apache.tiles.TilesException;
 import org.apache.tiles.access.TilesAccess;
 import org.apache.tiles.context.AbstractTilesApplicationContextFactory;
 import org.apache.tiles.definition.DefinitionsFactory;
 import org.apache.tiles.definition.digester.DigesterDefinitionsReader;
 import org.apache.tiles.evaluator.el.ELAttributeEvaluator;
 import org.apache.tiles.factory.AbstractTilesContainerFactory;
 import org.apache.tiles.factory.TilesContainerFactory;
 import org.apache.tiles.preparer.BasicPreparerFactory;
 import org.apache.tiles.servlet.context.ServletTilesApplicationContext;
 import org.apache.tiles.servlet.context.wildcard.WildcardServletTilesApplicationContextFactory;
 import org.apache.tiles.web.util.ServletContextAdapter;

 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.view.tiles2.SpringLocaleResolver;
public class SpringTilesConfigurer implements ServletContextAware, InitializingBean, DisposableBean {


    protected final static Logger logger = Logger
    .getLogger(SpringTilesConfigurer.class.getName());

    private final Properties tilesPropertyMap = new Properties();

    private ServletContext servletContext;

    private TilesApplicationContext tilesContext;

//    public void destroy() throws Exception {
//
//    }
//
//    public void afterPropertiesSet() throws Exception {
//
//    }
//
//    public void setServletContext(ServletContext arg0) {
//
//    }

    public SpringTilesConfigurer() {
            this.tilesPropertyMap.put(
                    AbstractTilesApplicationContextFactory.APPLICATION_CONTEXT_FACTORY_INIT_PARAM,
                    WildcardServletTilesApplicationContextFactory.class.getName());
            this.tilesPropertyMap.put(
                    TilesContainerFactory.PREPARER_FACTORY_INIT_PARAM,
                    BasicPreparerFactory.class.getName());
            this.tilesPropertyMap.put(
                    DefinitionsFactory.LOCALE_RESOLVER_IMPL_PROPERTY,
                    SpringLocaleResolver.class.getName());
            this.tilesPropertyMap.put(TilesContainerFactory.ATTRIBUTE_EVALUATOR_INIT_PARAM, ELAttributeEvaluator.class.getName());
            this.tilesPropertyMap.put(TilesContainerFactory.CONTAINER_FACTORY_MUTABLE_INIT_PARAM,
                    Boolean.toString(false));
    }

    /**
        * Set the Tiles definitions, i.e. the list of files containing the definitions.
        * Default is "/WEB-INF/tiles.xml".
        */
    public void setDefinitions(String[] definitions) {
        if (definitions != null) {
            String defs = StringUtils.arrayToCommaDelimitedString(definitions);
                logger.info("TilesConfigurer: adding definitions [" + defs + "]");
            this.tilesPropertyMap.put(DefinitionsFactory.DEFINITIONS_CONFIG, defs);
        }
    }

    /**
        * Set whether to validate the Tiles XML definitions. Default is "true".
        */
    public void setValidateDefinitions(boolean validateDefinitions) {
        this.tilesPropertyMap.put(DigesterDefinitionsReader.PARSER_VALIDATE_PARAMETER_NAME,
                Boolean.toString(validateDefinitions));
    }

    /**
        * Set the {@link org.apache.tiles.definition.DefinitionsFactory} implementation to use.
        * Default is {@link org.apache.tiles.definition.UrlDefinitionsFactory},
        * operating on definition resource URLs.
        * <p>Specify a custom DefinitionsFactory, e.g. a UrlDefinitionsFactory subclass,
        * to customize the creation of Tiles Definition objects. Note that such a
        * DefinitionsFactory has to be able to handle {@link java.net.URL} source objects,
        * unless you configure a different TilesContainerFactory.
        */
    public void setDefinitionsFactoryClass(Class<?> definitionsFactoryClass) {
        this.tilesPropertyMap.put(TilesContainerFactory.DEFINITIONS_FACTORY_INIT_PARAM,
                definitionsFactoryClass.getName());
    }

    /**
        * Set the {@link org.apache.tiles.preparer.PreparerFactory} implementation to use.
        * Default is {@link org.apache.tiles.preparer.BasicPreparerFactory}, creating
        * shared instances for specified preparer classes.
        * <p>Specify {@link SimpleSpringPreparerFactory} to autowire
        * {@link org.apache.tiles.preparer.ViewPreparer} instances based on specified
        * preparer classes, applying Spring's container callbacks as well as applying
        * configured Spring BeanPostProcessors. If Spring's context-wide annotation-config
        * has been activated, annotations in ViewPreparer classes will be automatically
        * detected and applied.
        * <p>Specify {@link SpringBeanPreparerFactory} to operate on specified preparer
        * <i>names</i> instead of classes, obtaining the corresponding Spring bean from
        * the DispatcherServlet's application context. The full bean creation process
        * will be in the control of the Spring application context in this case,
        * allowing for the use of scoped beans etc. Note that you need to define one
        * Spring bean definition per preparer name (as used in your Tiles definitions).
        * @see SimpleSpringPreparerFactory
        * @see SpringBeanPreparerFactory
        */
    public void setPreparerFactoryClass(Class<?> preparerFactoryClass) {
        this.tilesPropertyMap.put(TilesContainerFactory.PREPARER_FACTORY_INIT_PARAM,
                preparerFactoryClass.getName());
    }

    /**
        * Set whether to use a MutableTilesContainer for this application.
        * Default is "false".
        */
    public void setUseMutableTilesContainer(boolean useMutableTilesContainer) {
        this.tilesPropertyMap.put(TilesContainerFactory.CONTAINER_FACTORY_MUTABLE_INIT_PARAM,
                Boolean.toString(useMutableTilesContainer));
    }

    /**
        * Set Tiles properties (equivalent to the ServletContext init-params in
        * the Tiles documentation), overriding the default settings.
        */
    public void setTilesProperties(Properties tilesProperties) {
        CollectionUtils.mergePropertiesIntoMap(tilesProperties, this.tilesPropertyMap);
    }

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    /**
        * Creates and exposes a TilesContainer for this web application.
        * @throws TilesException in case of setup failure
        */
    public void afterPropertiesSet() throws TilesException {
        TilesContainer container = createTilesContainer();
    TilesAccess.setContainer(this.tilesContext, container);
    }

    /**
        * Create a TilesContainer for this web application.
        * @param context this web application's ServletContext
        * @return the TilesContainer to expose
        * @throws TilesException in case of setup failure
        */
    protected TilesContainer createTilesContainer() throws TilesException {
        ServletContextAdapter adaptedContext = new ServletContextAdapter(new DelegatingServletConfig());
        TilesApplicationContext preliminaryContext = new ServletTilesApplicationContext(adaptedContext);
        AbstractTilesApplicationContextFactory contextFactory = AbstractTilesApplicationContextFactory.createFactory(preliminaryContext);
        this.tilesContext = contextFactory.createApplicationContext(adaptedContext);
//        this.tilesContext.getInitParams().put(AbstractTilesApplicationContextFactory.class.getName(), WildcardServletTilesApplicationContextFactory.class.getName());
        AbstractTilesContainerFactory factory = AbstractTilesContainerFactory.getTilesContainerFactory(this.tilesContext);
        logger.info("Using tiles factory " + factory);
        logger.info("Tiles context init params " + new HashMap(this.tilesContext.getInitParams()));
        return factory.createContainer(this.tilesContext);
    }

    /**
        * Removes the TilesContainer from this web application.
        * @throws TilesException in case of cleanup failure
        */
    public void destroy() throws TilesException {
        TilesAccess.setContainer(this.tilesContext, null);
    }

    /**
        * Internal implementation of the ServletConfig interface, to be passed
        * to the wrapped servlet. Delegates to ServletWrappingController fields
        * and methods to provide init parameters and other environment info.
        */
    private class DelegatingServletConfig implements ServletConfig {

        public String getServletName() {
            return "TilesConfigurer";
        }

        public ServletContext getServletContext() {
            return servletContext;
        }

        public String getInitParameter(String paramName) {
            return tilesPropertyMap.getProperty(paramName);
        }

        public Enumeration<?> getInitParameterNames() {
            return tilesPropertyMap.keys();
        }
    }

     }